#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<dirent.h>
#include<ftw.h>
typedef int Myfunc(const char *,const struct stat * ,int);
static Myfunc myfunc;
static int myftw(char *,Myfunc *);
static int dopath(Myfunc *);
static long nreg,ndir,nblk,nchr,nfifo,nslink,nsock,ntot;

int main(int argc,char **argv)
{
    int     ret;
    if(argc != 2)
    {
        printf("usage: argv[0] <starting-pathname>\n");
    }    

    ret = myftw(argv[1],myfunc);
    ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;

    if(ntot == 0)
        ntot = 1;
    printf("regular files = %7ld, %5.2f%% \n",nreg,nreg*100.0/ntot);
    printf("directories = %7ld, %5.2f%% \n",ndir,ndir*100.0/ntot);
    printf("block special = %7ld,%5.2f%% \n",nblk,nblk*100.0/ntot);
    printf("char special = %7ld,%5.2f%% \n",nchr,nchr*100.0/ntot);
    printf("FIFOS = %7ld,%5.2f%% \n",nfifo,nfifo*100.0/ntot);
    printf("symbplic links = %7ld,%5.2f%% \n",nslink,nslink*100.0/ntot);
    printf("sockets = %7ld,%5.2f%% \n",nsock,nsock*100.0/ntot);
 
    return 0;
}

/*
#define FTW_F1 1
#define FTW_D 2
#define FTW_DNR 3
#define FTW_NS 4
*/

static char *fullpath;

static int myftw(char *pathname,Myfunc *func)
{
#ifdef PATH_MAX
    const int PATH_LEN = PATH_MAX;

#else
    const int PATH_LEN = 1024;

#endif
  
    fullpath = malloc(PATH_LEN);
    strncpy(fullpath,pathname,PATH_LEN);
    fullpath[PATH_LEN - 1] = '\0';
    int res = dopath(func);
    free(fullpath);
    return res;
}

static int dopath(Myfunc *func)
{
    struct stat statbuf;
    struct dirent *dirp;
    DIR *dp;
    int ret;
    char *ptr;
 
    if(lstat(fullpath,&statbuf)<0)
        return func(fullpath,&statbuf,FTW_NS);
    if(S_ISDIR(statbuf.st_mode)==0)
        return func(fullpath,&statbuf,FTW_F);
    ret = func(fullpath,&statbuf,FTW_D);
    if(ret != 0)
        return ret;
    ptr = fullpath +strlen(fullpath);
    *ptr++ = '/';
    *ptr = 0;

    if((dp = opendir(fullpath))==NULL)
        return func(fullpath,&statbuf,FTW_DNR);

    while((dirp = readdir(dp))!=NULL)
    {
        if(strcmp(dirp->d_name,".")==0||strcmp(dirp->d_name,"..")==0)
            continue;
        strcpy(ptr,dirp->d_name);
        ret = dopath(func);
        if(ret != 0)
           break;
    }
    ptr[-1] = 0;
    if(closedir(dp)<0)
        fprintf(stderr,"can't close directory %s\n",fullpath);
    return ret;
}

static int myfunc(const char *pathname,const struct stat *statptr,int type)
{
    switch(type)
    {
    case FTW_F:
      switch(statptr->st_mode & S_IFMT)
      {
      case S_IFREG:
        nreg++;
        printf("reg: %s\n",fullpath);
        break;
      case S_IFBLK:
        nblk++;
        printf("blk: %s\n",fullpath);
        break;
      case S_IFCHR:
        nchr++;
        printf("chr: %s\n",fullpath);
        break;
      case S_IFIFO:
        nfifo++;
        printf("fifo: %s\n",fullpath);
        break;
      case S_IFLNK:
        nslink++;
        printf("slink: %s\n",fullpath);
        break;
      case S_IFSOCK:
        nsock++;
        printf("socket: %s\n",fullpath);
        break;
      case S_IFDIR:
        fprintf(stderr,"FOR S_IFDIR for %s\n",pathname);
        exit(1);
      }
      break;
    case FTW_D:
      ndir++;
      printf("DIR: %s\n",fullpath);
      break;
    case FTW_DNR:
      fprintf(stderr,"can't read directory %s\n",pathname);
      break;
    case FTW_NS:
      fprintf(stderr,"stat error for %s\n",pathname);
      break;
    default:
      fprintf(stderr,"unknow type %d for pathname %s\n",type,pathname);
    }
    return 0;
}
